<template>
	<div class="app-code-editor" ref="appCodeEditor">
		<div
			:class="{
				'app-code-editor__header': true,
				[theme]: true,
				'app-code-editor__header--hidden': !showLanguage,
			}"
		>
			<div class="app-code-editor__header__left__toolbar">
				<div
					:class="{
						'left-toolbar__custom': true,
						'custom-select-visible': selectVisible,
					}"
				>
					<div class="custom-select-selection" @click="onSelectClick()">
						<div>
							<span class="custom-select-selected-value">{{
								presentLanguage
							}}</span>
							<down-outlined />
						</div>
					</div>
					<div
						class="custom-select-dropdown"
						:style="{ display: selectVisible ? 'block' : 'none' }"
					>
						<ul class="custom-select-dropdown-list">
							<li
								v-for="(item, index) in languages"
								:class="{
									'custom-select-item': true,
									'custom-select-item-selected': item === presentLanguage,
								}"
								:key="index"
								:value="item"
								@click="onLanguageChange(item)"
							>
								{{ item }}
							</li>
						</ul>
					</div>
				</div>
			</div>
			<div class="app-code-editor__header__right__toolbar">
				<div
					v-if="rightToolbarVisible"
					class="toolbar-item"
					title="全屏"
					@click="toolBarClick('fullScreen')"
				>
					<arrows-alt-outlined />
				</div>
				<div
					v-if="!rightToolbarVisible"
					class="toolbar-item"
					title="退出全屏"
					@click="toolBarClick('quitFullScreen')"
				>
					<fullscreen-exit-outlined />
				</div>
			</div>
		</div>
		<div ref="codeEditBox" class="app-code-editor__content"></div>
	</div>
</template>
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
import {
	ArrowsAltOutlined,
	FullscreenExitOutlined,
	DownOutlined,
} from '@ant-design/icons-vue';
import { codeEditorProps } from './code-editor';
import * as monaco from 'monaco-editor';
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import { IEvent } from '@/core';
// @ts-ignore: worker
self.MonacoEnvironment = {
	getWorker(_: string, label: string) {
		if (label === 'json') {
			return new jsonWorker();
		}
		if (['css', 'sass', 'less'].includes(label)) {
			return new cssWorker();
		}
		if (['html', 'handlebars', 'razor'].includes(label)) {
			return new htmlWorker();
		}
		if (['typescript', 'javascript'].includes(label)) {
			return new tsWorker();
		}
		return new EditorWorker();
	},
};
interface EditorEmit {
	(name: 'component-action', value: IEvent<string>): void;
}

const props = defineProps(codeEditorProps);
const emit = defineEmits<EditorEmit>();

// 编辑器容器
const appCodeEditor: any = ref();

// 编辑器实例
let codeEditor: monaco.editor.IStandaloneCodeEditor;

// 编辑器容器
const codeEditBox: any = ref();

// 编辑器高度
let codeEditBoxHeight = 300;

// 过滤事件抛值
let filterEmit = true;

// 控制下拉框
let selectVisible = ref(false);

// 当前使用语言
const presentLanguage: any = ref('');

// 语言列表
const languages = ref([
	'json',
	'javascript',
	'typescript',
	'css',
	'less',
	'sass',
	'java',
	'sql',
	'yaml',
] as string[]);

// 右侧工具栏项
const rightToolbarVisible = ref(true as boolean);

// 重置编辑器大小
const resize = () => {
	codeEditor.layout();
};

// 格式化函数
const formatCode = () => {
	if (!props.isFormatCode) {
		return;
	} else {
		codeEditor.updateOptions({ readOnly: false });
	}
	if (codeEditor) {
		codeEditor.trigger('anyString', 'editor.action.formatDocument', {});
	}
};

// 初始化高度
const initHeight = () => {
	if (!codeEditor) {
		return;
	}
	const editorContainer = appCodeEditor.value;
	const lineHeight =
		codeEditor?.getTopForLineNumber(2) - codeEditor?.getTopForLineNumber(1);
	const numberOfRows = codeEditor?.getModel()?.getLineCount() as number;
	const heightValue = lineHeight * numberOfRows + 100;
	const widthVlaue = editorContainer.offsetWidth;
	codeEditor.layout({ width: widthVlaue, height: heightValue });
};

// 全屏状态切换触发
const fullscreenchange = () => {
	if (document.fullscreenElement) {
		rightToolbarVisible.value = false;
	} else {
		rightToolbarVisible.value = true;
	}
};

const initCodeEditor = () => {
	codeEditor = monaco.editor.create(codeEditBox.value as any, {
		value: props.value,
		theme: props.theme,
		language: presentLanguage.value,
		readOnly: props.readOnly,
		lineNumbers: props.showLineNum ? 'on' : 'off',
		minimap: { enabled: props.showMinimap },
		scrollBeyondLastLine: props.isScroll,
	});

	// 设置默认格式化选项
	codeEditor.updateOptions({
		formatOnPaste: true,
		formatOnType: true,
	});

	// 监听 focus 事件
	codeEditor.onDidFocusEditorText(() => {
		filterEmit = false;
	});
	// 监听 blur 事件
	codeEditor.onDidBlurEditorText((event: any) => {
		filterEmit = true;
		valueChange(codeEditor);
	});
	// 监听值的变化
	codeEditor.getModel()?.onDidChangeContent(() => {
		if (filterEmit) {
			valueChange(codeEditor);
		}
	});
	formatCode();
	initHeight();
	valueChange(codeEditor);
	window.addEventListener('resize', resize);
	window.addEventListener('fullscreenchange', fullscreenchange);
};

// 工具栏点击事件
const toolBarClick = (key: string) => {
	const editorContainer = appCodeEditor.value;
	if (editorContainer && key === 'fullScreen') {
		editorContainer.requestFullscreen();
		codeEditBoxHeight = editorContainer.offsetHeight;
		rightToolbarVisible.value = false;
	}
	if (key === 'quitFullScreen') {
		document.exitFullscreen();
		editorContainer.style.height = `${codeEditBoxHeight}px`;
		rightToolbarVisible.value = true;
	}
};

// 处理下拉框点击
const onSelectClick = () => {
	selectVisible.value = !selectVisible.value;
};

// 适配下拉框隐藏
const handleClick = (event: any) => {
	const isLeftToolbarCustom =
		event.target.closest('.left-toolbar__custom') !== null;
	if (!isLeftToolbarCustom) {
		selectVisible.value = false;
	}
};

// 监听值变化
watch(
	() => props.value,
	(newVal: any, oldVal: any) => {
		if (newVal && newVal !== undefined && newVal !== oldVal) {
			if (codeEditor) {
				const currentPosition = codeEditor.getSelection();
				codeEditor.setValue(String(newVal));
				formatCode();
				initHeight();
				if (currentPosition) {
					codeEditor.setSelection(currentPosition);
				}
			}
		}
	},
	{ deep: true, immediate: true }
);

// 语言变化
const onLanguageChange = (languageValue: string) => {
	presentLanguage.value = languageValue;
	filterEmit = true;
	codeEditor.dispose();
	initCodeEditor();
	formatCode();
	selectVisible.value = false;
};

const valueChange = (codeEditorValue: any) => {
	const value = codeEditorValue.getValue();
	emit('component-action', {
		name: `${props.name}`,
		action: 'valueChange',
		data: value,
	});
};

onMounted(() => {
	presentLanguage.value = props.language;
	window.addEventListener('click', handleClick);
	initCodeEditor();
});

onBeforeUnmount(() => {
	codeEditor.dispose();
	window.removeEventListener('resize', resize);
	window.removeEventListener('fullscreenchange', fullscreenchange);
	window.removeEventListener('click', handleClick);
});
</script>
